package com.gee.spring.boot.rpf.client.handler;

import cn.hutool.json.JSONObject;
import com.gee.spring.boot.rpf.client.AbstractClient;
import com.gee.spring.boot.rpf.common.constant.ClientConstant;
import com.gee.spring.boot.rpf.controller.fico.RequestWrapper;
import com.gee.spring.boot.rpf.entity.*;
import com.gee.spring.boot.rpf.util.client.ClientUtil;
import com.gee.thread.flow.common.result.ExecuteState;
import com.gee.thread.flow.executor.ExecuteContext;
import com.gee.thread.flow.executor.WorkExecuteProperties;
import com.gee.thread.flow.handler.DefaultWorkHandler;
import com.gee.thread.flow.work.AbstractWork;
import com.gee.thread.flow.work.impl.EndWork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * desc:
 *
 * @author gee wrote on  2021-02-07 04:34:22
 */
@Component
public class CommonClientHandler extends DefaultWorkHandler<RequestWrapper> {

    private Logger logger = LoggerFactory.getLogger(CommonClientHandler.class);

    @Resource
    private ClientUtil clientUtil;

    @Override
    protected void postAfterFinishBeforeNextBegin(String currentWorkId, ExecuteState currentWorkExecuteState,
                                   Map<String,?> extraProperties, ExecuteContext<RequestWrapper> executeContext){
        List<TempVariableInfo> tempVariableInfoList = (List<TempVariableInfo>) extraProperties
                .get(ClientConstant.CLIENT_FLOW_TEMP_VARIABLE);
        RequestWrapper requestWrapper = executeContext.getVariable();
        JSONObject tempVariable = requestWrapper.getTempVariable();
        logger.info("{} calculate TempVariableInfo: {}", currentWorkId, tempVariableInfoList);
        for (TempVariableInfo tempVariableInfo : tempVariableInfoList) {
            Object factor = clientUtil.calculateTempVariable(currentWorkId, currentWorkExecuteState,
                    tempVariableInfo, requestWrapper, executeContext.getWorkResultMap());
            tempVariable.put(tempVariableInfo.getVariableName(), factor);
        }
        List<FactorInfo> list = (List<FactorInfo>) extraProperties.get(ClientConstant.CLIENT_FLOW_FACTOR_INFO);
        logger.info("{} calculate factors: {}", currentWorkId, list);
        JSONObject factors = requestWrapper.getFactors();
        for (FactorInfo factorInfo : list) {
            Object factor = clientUtil.calculateFactor(currentWorkId,currentWorkExecuteState,factorInfo,
                    requestWrapper, executeContext.getWorkResultMap());
            factors.put(factorInfo.getFactorName(), factor);
        }
    }

    @Override
    protected void editNextWork(AbstractWork<RequestWrapper,?,?> currentClient, ExecuteState currentWorkExecuteState,
                                ExecuteContext<RequestWrapper> executeContext) {
        Map<String, ?> extraProperties = currentClient.getExtraProperties();
        String clientId = currentClient.getId();
        List<NextClientEditor> nextClientEditorList = (List<NextClientEditor>) extraProperties
                .get(ClientConstant.CLIENT_FLOW_NEXT_CLIENT_EDITOR);
        AbstractWork<RequestWrapper,?,?> endWork = getEndWork(currentClient);
        logger.info("{} calculate nextWorkEditor: {}", clientId, nextClientEditorList);
        if (nextClientEditorList != null && nextClientEditorList.size() != 0){
            for (NextClientEditor nextClientEditor : nextClientEditorList) {
                RequestWrapper requestWrapper = executeContext.getVariable();
                List<ClientFlow> nextClientFlowList = clientUtil.calculateNextClient(clientId, currentWorkExecuteState,
                        nextClientEditor, requestWrapper, executeContext.getWorkResultMap());
                logger.info("{} calculated nextWorks: {}", clientId, nextClientFlowList);
                for (ClientFlow clientFlow : nextClientFlowList) {
                    setNextClient(currentClient, clientFlow, true, endWork, executeContext);
                }
            }
        }

    }

    private void setNextClient(AbstractWork<RequestWrapper,?,?> currentClient, ClientFlow clientFlow,
                               Boolean necessaryForNext, AbstractWork<RequestWrapper,?,?> endWork,
                               ExecuteContext<RequestWrapper> executeContext) {
        AbstractClient<?, ?> nextClient = clientUtil.createAbstractClient(clientFlow);
        nextClient.initExecuteContext(executeContext);
        if (nextClient.getWorkHandler() == null){
            nextClient.setWorkHandler(this);
        }

        //设置当前单元与下一级单元关联
        currentClient.getWorkExecuteProperties().getNextWorks().put(nextClient.getId(), nextClient);
        currentClient.getWorkExecuteProperties().getNecessaryForNextMap().put(nextClient.getId(),necessaryForNext);

        //设置下一级单元与当前单元关联
        WorkExecuteProperties<RequestWrapper> workExecuteProperties = nextClient.getWorkExecuteProperties();
        workExecuteProperties.getPreWorks().put(currentClient.getId(), currentClient);


        //是否有下一级单元
        List<RefClientFlow> clientRelativeList = clientUtil.getRefClientFlowList(clientFlow.getSerialNo());
        if (clientRelativeList == null || clientRelativeList.size() == 0){
            workExecuteProperties.getNextWorks().put(EndWork.END_WORK_ID, endWork);
            workExecuteProperties.getNecessaryForNextMap().put(EndWork.END_WORK_ID, true);
            endWork.getWorkExecuteProperties().getPreWorks().put(nextClient.getId(),nextClient);
        }else {
            clientRelativeList.forEach(nextRefClientFlow -> {
                ClientFlow nextClientFlow = clientUtil.getClientFlow(nextRefClientFlow.getNextClientFlowSerialNo());
                setNextClient(nextClient, nextClientFlow, nextRefClientFlow.getNecessaryForNext(), endWork, executeContext);
            });
        }
    }

    private AbstractWork<RequestWrapper, ?, ?> getEndWork(AbstractWork<RequestWrapper, ?, ?> currentWork){
        Map<String, AbstractWork<RequestWrapper, ?, ?>> nextWorks = currentWork.getWorkExecuteProperties().getNextWorks();
        if (nextWorks.containsKey(EndWork.END_WORK_ID)){
            return nextWorks.get(EndWork.END_WORK_ID);
        }
        AbstractWork<RequestWrapper, ?, ?> oneNextWork = nextWorks.values().stream().findAny().get();
        return getEndWork(oneNextWork);
    }
}
